package com.ydlclass.bootstrap;

import com.ydlclass.codec.YrpcMessageDecoder;
import com.ydlclass.codec.YrpcMessageEncoder;
import com.ydlclass.configuration.YrpcConfiguration;
import com.ydlclass.configuration.YrpcConfigurationHolder;
import com.ydlclass.constants.YrpcConstants;
import com.ydlclass.exception.YrpcBootstrapException;
import com.ydlclass.handler.YrpcRequestMessageHandler;
import com.ydlclass.message.MethodKey;
import com.ydlclass.registry.Registry;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author it楠老师
 * @createTime 2023-03-09
 */
@Slf4j
public class ServiceBootstrap {

    // 维护全局上下文
    private static YrpcConfiguration configuration;

    // 定义服务的名称
    private String serviceName = YrpcConstants.DEFAULT_SERVICE_NAME;

    // 维护对外暴露服务列表
    private Map<MethodKey, Object> exposeServices = new ConcurrentHashMap<>(128);

    // 默认暴露的端口
    private int port = 8999;

    // 每个服务以单例的形式存在
    private static ServiceBootstrap serviceBootstrap = new ServiceBootstrap();


    private ServiceBootstrap() {

    }

    /**
     * 获得单例实例
     * @return ServiceBootstrap单实例
     */
    public static ServiceBootstrap getInstance() {
        // 配置默认的上下文
        YrpcConfiguration.Builder builder = new YrpcConfiguration.Builder();
        configuration = builder.build();
        YrpcConfigurationHolder.set(configuration);
        return serviceBootstrap;
    }

    /**
     * 配置服务名称
     * @param serviceName 服务名称
     * @return this实例
     */
    public ServiceBootstrap serviceName(String serviceName) {
        if (serviceName == null) {
            throw new YrpcBootstrapException("This serviceName is not valid");
        }
        this.serviceName = serviceName;
        if (log.isDebugEnabled()) {
            log.debug("You have registered for a service named {}", serviceName);
        }
        return this;
    }

    /**
     * 使用编码方式指定注册中心
     *
     * @param registry
     * @return
     */
    public ServiceBootstrap registry(Registry registry) {
        if (registry == null) {
            throw new YrpcBootstrapException("You must specify a valid registry.");
        }
        YrpcConfigurationHolder.get().setRegistry(registry);
        return this;
    }

    /**
     * 指定传输报文的协议
     *
     * @return
     */
    public ServiceBootstrap protocol() {

        return this;
    }

    public ServiceBootstrap service(Class<?> clazz) {
        return this.service(clazz, null, null);
    }

    /**
     * 指定一个对外暴露的接口
     * @return this
     */
    public ServiceBootstrap service(Class<?> clazz, Class<?>[] constructorTypes, Object[] constructorParameters) {
        if (clazz == null) {
            throw new YrpcBootstrapException("You cannot pass in an empty class instance.");
        }
        try {
            // 实例化
            Object serviceInstance;
            if (constructorTypes == null && constructorParameters == null) {
                Constructor<?> constructor = clazz.getConstructor();
                serviceInstance = constructor.newInstance();
            } else {
                Constructor<?> constructor = clazz.getConstructor(constructorTypes);
                serviceInstance = constructor.newInstance(constructorParameters);
            }
            // 查询当前暴露的服务实现的接口，如果存在接口则以接口的形式暴露
            Class<?>[] interfaces = clazz.getInterfaces();
            if (interfaces.length == 0) {
                throw new YrpcBootstrapException("Your exposed classes must implement interfaces！");
            }
            for (Class<?> itf : interfaces) {
                Method[] methods = itf.getMethods();
                for (Method method : methods) {
                    MethodKey methodKey = new MethodKey();
                    methodKey.setInterfaceName(itf.getName());
                    methodKey.setMethodName(method.getName());
                    methodKey.setParameterTypes(method.getParameterTypes());
                    exposeServices.put(methodKey, serviceInstance);
                }
            }
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
                 InvocationTargetException e) {
            log.error("An exception has occurred while loading exposed services!", e);
        }
        return this;
    }

    /**
     * 扫描包路径，将包下的类全部暴露
     * @param packageName 包名
     * @return this
     */
    public ServiceBootstrap scan(String packageName) {
        if(packageName == null || "".equals(packageName)){
            throw new YrpcBootstrapException("You must pass in a legal package name.");
        }
        String basePath = resolve(packageName);
        List<Class<?>> classes = findClassesFromBasePath(basePath);
        for (Class<?> aClass : classes) {
            this.service(aClass);
        }
        return this;
    }

    private String resolve(String packageName){
        return "";
    }

    /**
     * 根据根路径获取下边的类的集合
     * @param basePath 根路径
     * @return 所有的class
     */
    private List<Class<?>> findClassesFromBasePath(String basePath) {

        URL resource = Thread.currentThread().getContextClassLoader().getResource(basePath);
        String path = resource.getPath();
        // todo 递归  com/sss/xxx.class  -> com.sss.xxx.class  Class.forName

        return new ArrayList<>();
    }


    /**
     * 向注册中心注册服务
     */
    private void publish() {
        if(configuration.getRegistry() == null){
            throw new YrpcBootstrapException("You must have a available registry，You may not have pre-registered.");
        }

        if (exposeServices == null || exposeServices.size() == 0){
            throw new YrpcBootstrapException("There is no interface to expose to the outside project.");
        }

        // 注册服务
        for (Map.Entry<MethodKey,Object> entry :this.exposeServices.entrySet()){
            configuration.getRegistry().register(entry.getKey().getInterfaceName());
        }
    }

    /**
     * 启动工程
     */
    public void startup() {
        YrpcConfigurationHolder.get().setPort(port);
        this.startup(port);
    }

    public void startup(int port) {
        YrpcConfigurationHolder.get().setPort(port);
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();

        try {
            Long start = System.currentTimeMillis();
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.group(boss, worker);
            serverBootstrap.childHandler(new ChannelInitializer<>() {
                @Override
                protected void initChannel(Channel channel) throws Exception {
                    channel.pipeline()
                            .addLast(new LoggingHandler(LogLevel.DEBUG))
                            .addLast(new YrpcMessageDecoder())
                            .addLast(new YrpcMessageEncoder())
                            .addLast(new YrpcRequestMessageHandler());
                }
            });

            // todo 发布服务到注册中心,netty服务完全启动后发布
            Channel channel = serverBootstrap.bind(port).sync().channel();
            publish();
            log.info("yrpc server is started in {} milliseconds",System.currentTimeMillis() - start);
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("server error", e);
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }


    }

    public Object getExposeInstance(MethodKey methodKey) {
        return exposeServices.get(methodKey);
    }
}
